/*
 * ULSF Sagittarius IDA Jones Panel
 * Copyright (C) 2002, Kevin Jones, all rights reserved.
 * Copyright (C) 2012, John Pritchard, all rights reserved.
 * 
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
package sagittarius.airfoil;

import sagittarius.Field;
import sagittarius.Visualization;

import json.Json;

import com.badlogic.gdx.graphics.Mesh;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * Basic airfoil geometry
 * 
 * @see NACA
 * @see UIUC
 * @see sagittarius.panel.Airfoil
 * @see sagittarius.panel.Panel
 */
public abstract class Airfoil
    extends ModelVertices
    implements Cloneable,
               Visualization
{
    public interface Properties {

        public interface Names
            extends Properties
        {
            public final static String Configuration = "sagittarius.airfoil.Airfoil";
            public final static String Identifier = "sagittarius.airfoil.Airfoil.Identifier";
        }
        public interface Defaults
            extends Properties
        {
            public final static String Configuration = "sagittarius.airfoil.NACA";
            public final static String Identifier = "2509";
        }
    }
    /**
     * System property "sagittarius.airfoil.Airfoil.Identifier" is
     * employed via {@link #Identifier()} as the principal design
     * parameter within an Airfoil subclass.
     */
    public final static String Identifier(){

        String identifier = System.getProperty(Properties.Names.Identifier);
        if (null == identifier)
            identifier = Properties.Defaults.Identifier;

        return identifier;
    }
    public final static void Identifier(String value){
        if (null != value){
            value = value.trim();
            if (0 != value.length()){
                System.setProperty(Properties.Names.Identifier,value);
            }
        }
    }
    /**
     * System property "sagittarius.airfoil.Airfoil" is employed via
     * {@link #Constructor()} as a classname for a class with a public
     * constructor with one parameter {@link Visualization}.
     * 
     * @see java.lang.Class#forName(String)
     */
    public final static String Configuration(){

        String classname = System.getProperty(Properties.Names.Configuration);
        if (null == classname)
            classname = Properties.Defaults.Configuration;

        return classname;
    }
    public final static void Configuration(String classname){
        try {
            Class<?> oclas = (Class<?>)Class.forName(classname);
            if (Airfoil.class.isAssignableFrom(oclas)){

                System.setProperty(Properties.Names.Configuration,classname);
            }
            else
                throw new IllegalArgumentException(classname);
        }
        catch (ClassNotFoundException exc){
            throw new IllegalArgumentException(classname,exc);
        }
    }
    /**
     * Dynamic configuration function used by {@link Body} to
     * construct airfoil.
     */
    public final static Airfoil Constructor(Visualization data){

        return Airfoil.Constructor(Airfoil.Configuration(),data);
    }
    /**
     * Dynamic binding requires public constructor having parameter {@link Visualization}
     */
    public final static Class ConstructorParameter = Visualization.class;

    public final static Airfoil Constructor(String classname, Visualization data){
        if (null != classname && null != data){
            try {
                Class<?> oclas = (Class<?>)Class.forName(classname);
                if (Airfoil.class.isAssignableFrom(oclas)){
                    Class<Airfoil> clas = (Class<Airfoil>)oclas;
                    Constructor<Airfoil> ctor = clas.getConstructor(ConstructorParameter);
                    return ctor.newInstance(data);
                }
                else
                    throw new IllegalArgumentException(classname);
            }
            catch (NoSuchMethodException exc){
                throw new IllegalStateException(classname,exc);
            }
            catch (ClassNotFoundException exc){
                throw new IllegalStateException(classname,exc);
            }
            catch (SecurityException exc){
                throw new IllegalStateException(classname,exc);
            }
            catch (InstantiationException exc){
                throw new IllegalStateException(classname,exc);
            }
            catch (IllegalAccessException exc){
                throw new IllegalStateException(classname,exc);
            }
            catch (InvocationTargetException exc){
                Throwable cause = exc.getCause();
                if (cause instanceof ThreadDeath)
                    throw (ThreadDeath)cause;
                else
                    throw new IllegalStateException(classname,cause);
            }
        }
        else
            throw new IllegalArgumentException(classname);
    }



    public Airfoil(Visualization data){
        super();
    }


    /**
     * Ready for {@link #transform(float,float)} and {@link
     * #translate(float)}.
     */
    public boolean isInitialized(){
        return this.isInitializedModelVertices();
    }
    /**
     * Ready for {@link #getSectionVertices3At(float)}
     */
    public boolean isTransformed(){
        return this.isTransformedModelVertices();
    }
    /**
     * Model construction parameters definition
     */
    protected AirfoilVertices define(int np){

        return this.setModelVertices(new AirfoilVertices(Geometry.Model,np));
    }
    /**
     * Construct model before transformation to objective location and
     * dimension
     * 
     * @see #transform(float,float)
     * @see #translate(float)
     */
    public abstract void init(int np);

    public final boolean isInitializedModelVertices(){
        return this.isInitializedModelVertices(Geometry.Model);
    }
    public final boolean isTransformedModelVertices(){
        return this.isTransformedModelVertices(Geometry.Model);
    }
    /**
     * Model coordinates between zero and one, ordered from the
     * trailing edge at (1,0) to the leading edge at (0,0) -- in
     * clockwise order bottom then top.
     *
     * @return Vertices in (X,Z)+ order, cloned from the array stored
     * internally.
     */
    public final AirfoilVertices getModelVertices(){

        return this.getModelVertices(Geometry.Model);
    }
    public final int countModelVertices(){

        return this.countModelVertices(Geometry.Model);
    }
    /**
     * The section is generated from the model
     */
    public final boolean isInitializedSectionVertices(){
        return this.isInitializedModelVertices(Geometry.Section);
    }
    public final boolean isTransformedSectionVertices(){
        return this.isTransformedModelVertices(Geometry.Section);
    }
    /**
     * Section coordinates between zero and one, ordered from the
     * leading edge at (1,0) to the trailing edge at (0,0) -- in
     * counter-clockwise order top then bottom.
     *
     * @return Vertices in (X,Z)+ order, cloned from the array stored
     * internally.
     */
    public final AirfoilVertices getSectionVertices(){

        return this.getModelVertices(Geometry.Section);
    }
    public final AirfoilVertices setSectionVertices(AirfoilVertices section){

        return this.setModelVertices(section);
    }
    public final int countSectionVertices(){

        return this.countModelVertices(Geometry.Section);
    }
    /**
     * After init or transform, and before getting vertices, translate
     * the objective (lateral foil) section location in the Y
     * dimension.
     *
     * @param y Scaled Y coordinate for this section
     *
     * @see #init(int)
     * @see #transform(float,float)
     */
    public final void translate(float y){

        this.getSectionVertices().modelTranslate(y);
    }
    /**
     * Transform a section to the objective (lateral foil) section
     * location and dimension.
     *
     * Because the section chord is 1.0, the scaled chord is also the
     * scale factor.
     * 
     * @param tex Scaled X coordinate of trailing edge
     * @param chord Scaled chord of this section
     *
     * @see #init(int)
     * @see #translate(float)
     */
    public final void transform(float tex, float chord){

        this.getSectionVertices().modelTransform(tex,chord);
    }
    /**
     * Intersection
     * 
     * @param x Coordinate in X
     *
     * @return One or two vec3-coordinates: (Edge) or (Bottom,Top).
     * Otherwise null for not found (out of range).
     * 
     * @see #init(int)
     * @see #transform(float,float)
     * @see #translate(float)
     */
    public final float[] getSectionVertices3At(float x){

        return this.getSectionVertices().getSectionVertices3At(x);
    }
    public void list(){
        AirfoilVertices vertices = this.getSectionVertices();
        if (null != vertices)
            vertices.list();
        else {
             vertices = this.getModelVertices();
             if (null != vertices)
                 vertices.list();
        }
    }
    public Mesh getModelMesh(){
        AirfoilVertices vertices = this.getModelVertices();
        if (null != vertices)
            return vertices.getMesh();
        else
            return null;
    }
    public Mesh getSectionMesh(){
        AirfoilVertices vertices = this.getSectionVertices();
        if (null != vertices)
            return vertices.getMesh();
        else
            return null;
    }
    /**
     * @return  Model mesh
     */
    public Mesh getMesh(){

        return this.getModelMesh();
    }
    @Override
    public Airfoil clone(){

        return (Airfoil)super.clone();
    }
    /*
     */
    public float get(Field field){
        throw new UnsupportedOperationException();
    }
    public float getLength(){
        throw new UnsupportedOperationException();
    }
    public float getRadius(){
        throw new UnsupportedOperationException();
    }
    public float getSpan(){
        throw new UnsupportedOperationException();
    }
    public float getAlpha(){
        throw new UnsupportedOperationException();
    }
    public float getBeta(){
        throw new UnsupportedOperationException();
    }
    public float getGamma(){
        throw new UnsupportedOperationException();
    }
    public float getDelta(){
        throw new UnsupportedOperationException();
    }
    public float getEpsilon(){
        throw new UnsupportedOperationException();
    }
    public float getZeta(){
        throw new UnsupportedOperationException();
    }
    public float getEta(){
        throw new UnsupportedOperationException();
    }
    public float getTheta(){
        throw new UnsupportedOperationException();
    }
    public float getIota(){
        throw new UnsupportedOperationException();
    }
    public float getKappa(){
        throw new UnsupportedOperationException();
    }
    public String getVolume(){
        throw new UnsupportedOperationException();
    }
    public String getAirfoilConfiguration(){
        return Airfoil.Configuration();
    }
    public String getAirfoilIdentifier(){
        return Airfoil.Identifier();
    }
    public void generate(){

        this.generate(Geometry.Model,Geometry.Section);
    }
    public void terminal(){
    }
    public boolean waitfor(){
        return true;
    }
    public Json toJson(){
        throw new UnsupportedOperationException();
    }
    public boolean fromJson(Json json){
        throw new UnsupportedOperationException();
    }
}
